package com.webcamtracker.color.segmentation;

import com.webcamtracker.configuration.RootPref;
import com.webcamtracker.image.Color;
import com.webcamtracker.image.color.rgb.RGBColor;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.util.*;
import java.util.prefs.Preferences;


public class ColorSegmentator {

    // The input image and its dimensions.
    private int width, height;

    // A matrix for the pixel values, one for the selected labels, one which indicates
    // whether a pixel already has a label.
    private int[] pixels;
    private int[] labels;
    private boolean[] analyzed;

    // Counters for pixels in each region
    private Map<Integer, Segment> segmentsMap;
    private SortedSet<Segment> segments;


    //Map: region number > points of the region
    private Map<Color, SortedSet<Segment>> regions;

    private static Preferences pref = RootPref.getRootPref().node("segmentation");

    public Map<Color, SortedSet<Segment>> segment(BufferedImage image) {
        initVariables(image);
        final int min = 100;//pref.getInt("min", 100);

        int numberOfRegion = 0;
        Segment currentSegment = null;

        final java.util.List<Point> mustDo = new LinkedList<Point>();
        final java.util.List<Point> mustSearchSegmenet = new LinkedList<Point>();
        boolean segmentAdded = false;
        mustSearchSegmenet.add(new Point(0, 0));
        analyzed[0] = true;
        while (!mustSearchSegmenet.isEmpty()) {
            Point searchPoint = mustSearchSegmenet.remove(0);
            final int y = searchPoint.y;
            final int x = searchPoint.x;

            // Is this pixel unlabeled?
            if (labels[y * width + x] < 0) {
                numberOfRegion++;
                final RGBColor color = new RGBColor(image.getRGB(x, y));
                currentSegment = new Segment(color, numberOfRegion);
                segmentAdded = false;
                mustDo.add(new Point(x, y));
                labels[y * width + x] = numberOfRegion; // label it as one on a new region

            }
            assert currentSegment != null;
            // Check all the pixels on the stack. There may be more than one!
            while (mustDo.size() > 0) {
                Point thisPoint = mustDo.remove(0);

                // Check 8-neighborhood
                for (int th = -1; th <= 1; th++)
                    for (int tw = -1; tw <= 1; tw++) {
                        int rx = thisPoint.x + tw;
                        int ry = thisPoint.y + th;
                        // Skip pixels outside of the image.
                        if ((rx >= 0) && (ry >= 0) && (ry < height) && (rx < width)) {
                            if (labels[ry * width + rx] < 0) {
                                final int pixelA = pixels[ry * width + rx];
                                final int pixelB = pixels[thisPoint.y * width + thisPoint.x];
                                final Point point = new Point(rx, ry);
                                if (pixelA == pixelB) {
                                    mustDo.add(point);
                                    labels[ry * width + rx] = numberOfRegion;
                                    currentSegment.addPoint(point);
                                } else {
                                    if (!analyzed[ry * width + rx]) {
                                        mustSearchSegmenet.add(point);
                                        analyzed[ry * width + rx] = true;
                                    }
                                }
                            }
                        }

                    } // ended neighbors checking
            } // ended stack scan

            if (!segmentAdded && currentSegment.getPoints().size() > min) {
                addSegment(currentSegment.getColor(), currentSegment);
                segments.add(currentSegment);
                segmentsMap.put(numberOfRegion, currentSegment);
                segmentAdded = true;
            }

        }

//        paintImage(image);

        return regions;
    }

    public SortedSet<Segment> getSegments() {
        return segments;
    }

    private void addSegment(Color color, Segment currentSegment) {
        SortedSet<Segment> segments = regions.get(color);
        if (segments == null) {
            segments = new TreeSet<Segment>(new SizeSegmentComparator());
            regions.put(color, segments);
        }
        segments.add(currentSegment);
    }

    private void initVariables(BufferedImage image) {
        this.width = image.getWidth();
        this.height = image.getHeight();
        this.segments = new TreeSet<Segment>(new SizeSegmentComparator());

        this.labels = new int[width * height];
        this.analyzed = new boolean[width * height];

        for (int i1 = 0; i1 < labels.length; i1++) {
            labels[i1] = -1;
        }
        for (int i1 = 0; i1 < analyzed.length; i1++) {
            analyzed[i1] = false;
        }


        PixelGrabber pg = new PixelGrabber(image, 0, 0, -1, -1, true);

        try {
            pg.grabPixels();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }


        this.pixels = (int[]) pg.getPixels();

        this.segmentsMap = new HashMap<Integer, Segment>();
        this.regions = new HashMap<Color, SortedSet<Segment>>();
    }


    public Map<Integer, Segment> getSegmentsMap() {
        return segmentsMap;
    }

    public static void main(String[] args) {
        Point point = new Point(10, 10);
        int radix = 1;
        while (radix <= 5) {

            for (int th = -radix; th <= radix; th++) {
                System.out.println("X " + (point.x + th) + " Y " + (point.y + radix));
            }
            for (int th = -radix; th <= radix; th++) {
                System.out.println("X " + (point.x + th) + " Y " + (point.y - radix));
            }
            System.out.println("******************************************");
            for (int th = -radix; th <= radix; th++) {
                System.out.println("Y " + (point.y + th) + " X " + (point.x - radix));
            }
            for (int th = -radix; th <= radix; th++) {
                System.out.println("Y " + (point.y + th) + " X " + (point.x + radix));
            }
            radix++;
            System.out.println("******************************************");
        }
    }


}
